/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author            Notes
 * 2022-01-25     iysheng           first version
 */

#include <board.h>
#include <sys/time.h>
#define DBG_TAG             "drv.rtc"
#define DBG_LVL             DBG_INFO

#include <rtdbg.h>

#ifdef RT_USING_RTC

#define  RTC_CLOCK_SOURCE_LXTAL 1
//#define RTC_CLOCK_SOURCE_IRC32K 1
__IO uint32_t prescaler_a =0, prescaler_s=0;

typedef struct {
    struct rt_device rtc_dev;
} gd32_rtc_device;

static gd32_rtc_device g_gd32_rtc_dev;

static time_t get_rtc_timestamp(void)
{
	  rtc_parameter_struct rtc_timestamp ={0}; //add by rf
		struct tm tm_new={0};
		
		rtc_current_time_get(&rtc_timestamp);
		
		tm_new.tm_sec = rtc_timestamp.second;
		tm_new.tm_min = rtc_timestamp.minute;
		tm_new.tm_hour = rtc_timestamp.hour;
		tm_new.tm_mday = rtc_timestamp.date;
		tm_new.tm_mon = rtc_timestamp.month - 1;
		tm_new.tm_year = rtc_timestamp.year + 100;
	 
		LOG_D("get rtc time. ");
		return mktime(&tm_new);
}

static rt_err_t set_rtc_timestamp(time_t time_stamp)
{
	
	  rtc_parameter_struct rtc_initpara;
	
	struct tm*  p_tm;
	p_tm = localtime(&time_stamp);
	if(p_tm->tm_year < 100)
	{
	  return -RT_ERROR;
	}	
	
	rtc_initpara.factor_asyn = prescaler_a;
	rtc_initpara.factor_syn = prescaler_s;
	rtc_initpara.display_format = RTC_24HOUR;
	rtc_initpara.am_pm = RTC_AM;
	
	rtc_initpara.second = p_tm->tm_sec;
	rtc_initpara.minute = p_tm->tm_min;
	rtc_initpara.hour = p_tm->tm_hour;
	rtc_initpara.date = p_tm->tm_mday;
	rtc_initpara.month = p_tm->tm_mon + 1;
	rtc_initpara.year = p_tm->tm_year - 100;
	rtc_initpara.day_of_week = p_tm->tm_wday + 1;
	
	if(rtc_init(&rtc_initpara) == ERROR)
	{
	  return -RT_ERROR;
	}
	
	return RT_EOK;
	
}

static rt_err_t rt_gd32_rtc_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = RT_EOK;

    RT_ASSERT(dev != RT_NULL);
    switch (cmd)
    {
    case RT_DEVICE_CTRL_RTC_GET_TIME:
       *(rt_uint32_t *)args = get_rtc_timestamp();
        break;
    case RT_DEVICE_CTRL_RTC_SET_TIME:
        if (set_rtc_timestamp(*(rt_uint32_t *)args))
        {
            result = -RT_ERROR;
        }
        break;
    }

    return result;
}

#ifdef RT_USING_DEVICE_OPS  
const static struct rt_device_ops g_gd32_rtc_ops =
{
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    rt_gd32_rtc_control
};
#endif

static int rt_hw_rtc_init(void)
{
    rt_err_t ret= RT_EOK;
    time_t rtc_counter;

    rcu_periph_clock_enable(RCU_PMU);
    pmu_backup_write_enable();
    rcu_periph_clock_enable(RCU_BKPSRAM); //BUG RCU_BKPI - RCU_BKPSRAM
	

   rtc_counter = get_rtc_timestamp();
    /* once the rtc clock source has been selected, if can't be changed
     * anymore unless the Backup domain is reset */
    rcu_bkp_reset_enable();
    rcu_bkp_reset_disable();
#if RTC_CLOCK_SOURCE_IRC32K
    rcu_osci_on(RCU_IRC32K);
    if(SUCCESS == rcu_osci_stab_wait(RCU_IRC32K))
		{
		  rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);
		}			
	
		 prescaler_s = 0x13F;
		 prescaler_a = 0x63;
		
#elif RTC_CLOCK_SOURCE_LXTAL
     		
    //rcu_periph_clock_enable(RCU_RTC);
    rcu_osci_on(RCU_LXTAL);
    if (SUCCESS == rcu_osci_stab_wait(RCU_LXTAL))
    {
			//rt_kprintf("rcu_osci_stab_wait success\n");
        /* set lxtal as rtc clock source */
        rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
    }
		 prescaler_s = 0xFF;
		 prescaler_a = 0x7F;
		
		#else
		LOG_E("RTC CLOCK SOURCE shoulde be defined");
#endif
		
		rcu_periph_clock_enable(RCU_RTC);
		rtc_register_sync_wait();
    set_rtc_timestamp(rtc_counter);
    
		
#ifdef RT_USING_DEVICE_OPS
    g_gd32_rtc_dev.rtc_dev.ops         = &g_gd32_rtc_ops;
#else
    g_gd32_rtc_dev.rtc_dev.init        = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.open        = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.close       = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.read        = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.write       = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.control     = rt_gd32_rtc_control;
#endif
    g_gd32_rtc_dev.rtc_dev.type        = RT_Device_Class_RTC;
    g_gd32_rtc_dev.rtc_dev.rx_indicate = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.tx_complete = RT_NULL;
    g_gd32_rtc_dev.rtc_dev.user_data   = RT_NULL;

    ret = rt_device_register(&g_gd32_rtc_dev.rtc_dev, "rtc", \
        RT_DEVICE_FLAG_RDWR);
    if (ret != RT_EOK)
    {
        LOG_E("failed register internal rtc device, err=%d", ret);
    }

    return ret;
}
INIT_DEVICE_EXPORT(rt_hw_rtc_init);
#endif
